#include "WindowsWindow.h"
#include "hzpch.h"

#include "Hazel/src/Hazel/event/ApplicationEvent.h"
#include "Hazel/src/Hazel/event/KeyEvent.h"
#include "Hazel/src/Hazel/event/MouseEvent.h"

#include "Hazel/src/Hazel/Render/Renderer.h"

// #include "glad/glad.h"
#include "../OpenGL/OpenGLContext.h"

namespace Hazel
{

static bool s_GLFWInitialized = false;

static void GLFWErrorCallback(int error, const char *description)
{
    HZ_CORE_ERROR("GLFW ERROR ({0}): {1}", error, description);
}

Scope<Window> Window::Create(const WindowProps &props)
{
    return CreateScope<WindowsWindow>(props);
}
WindowsWindow::WindowsWindow(const WindowProps &props)
{
    HZ_PROFILE_FUNC();
    Init(props);
}
WindowsWindow::~WindowsWindow()
{
    HZ_PROFILE_FUNC();
    Shutdown();
}
void WindowsWindow::OnUpdate()
{
    HZ_PROFILE_FUNC();

    glfwPollEvents();
    m_Context->SwapBuffers();
}
void WindowsWindow::SetVSync(bool enabled)
{
    HZ_PROFILE_FUNC();
    if (enabled)
        glfwSwapInterval(1);
    else
        glfwSwapInterval(0);

    m_Data.VSync = enabled;
}
bool WindowsWindow::IsVSync() const
{
    return m_Data.VSync;
}
void WindowsWindow::Init(const WindowProps &props)
{
    HZ_PROFILE_FUNC();

    m_Data.Title = props.Title;
    m_Data.Width = props.Width;
    m_Data.Height = props.Height;

    HZ_CORE_INFO("creating Window {0} ({1},{2})", m_Data.Title, m_Data.Width, m_Data.Height);

    if (!s_GLFWInitialized)
    {
        // todo:glfwTerminate on system shutdowm
        int success = glfwInit();

        s_GLFWInitialized = true;

        glfwSetErrorCallback(GLFWErrorCallback);
    }

    m_Window = glfwCreateWindow((int)props.Width, (int)props.Height, m_Data.Title.c_str(), nullptr, nullptr);
    m_Context = new OpenGLContext(m_Window);

    m_Context->Init();

    glfwMakeContextCurrent(m_Window);
    int status = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
    if (!status)
    {
        HZ_CORE_ERROR("fail to initialize GLAD!");
    }

    glfwSetWindowUserPointer(m_Window, &m_Data);
    SetVSync(false);

    // set glfw callback
    glfwSetWindowSizeCallback(m_Window, [](GLFWwindow *window, int width, int height) {
        WindowData &data = *(WindowData *)glfwGetWindowUserPointer(window);
        data.Width = width;
        data.Height = height;

        WindowResizeEvent event(width, height);

        //   HZ_CORE_WARN("{0},{1}", width, height);
        data.EventCallBack(event);
    });

    glfwSetWindowCloseCallback(m_Window, [](GLFWwindow *window) {
        WindowData &data = *(WindowData *)glfwGetWindowUserPointer(window);

        WindowCloseEvent event;
        data.EventCallBack(event);
    });

    glfwSetKeyCallback(m_Window, [](GLFWwindow *window, int key, int scancode, int action, int mods) {
        WindowData &data = *(WindowData *)glfwGetWindowUserPointer(window);

        switch (action)
        {
        case GLFW_PRESS: {
            KeyPressedEvent event(key, 0);
            data.EventCallBack(event);

            break;
        }
        case GLFW_RELEASE: {
            KeyReleasedEvent event(key);
            data.EventCallBack(event);

            break;
        }
        case GLFW_REPEAT: {
            KeyPressedEvent event(key, 1);
            data.EventCallBack(event);

            break;
        }
        default:
            break;
        }
    });

    glfwSetCharCallback(m_Window, [](GLFWwindow *window, unsigned int keycode) {
        WindowData &data = *(WindowData *)glfwGetWindowUserPointer(window);
        KeyTypedEvent event(keycode);
        data.EventCallBack(event);
    });
    glfwSetMouseButtonCallback(m_Window, [](GLFWwindow *window, int button, int action, int mods) {
        WindowData &data = *(WindowData *)glfwGetWindowUserPointer(window);

        switch (action)
        {
        case GLFW_PRESS: {
            MouseButtonPressEvent event(button);
            data.EventCallBack(event);
            break;
        }
        case GLFW_RELEASE: {
            MouseButtonReleasedEvent event(button);
            data.EventCallBack(event);
            break;
        }
        }
    });

    glfwSetScrollCallback(m_Window, [](GLFWwindow *window, double x_offset, double y_offset) {
        WindowData &data = *(WindowData *)glfwGetWindowUserPointer(window);

        MouseScrolledEvent event((float)x_offset, (float)y_offset);
        data.EventCallBack(event);
    });

    glfwSetCursorPosCallback(m_Window, [](GLFWwindow *window, double x, double y) {
        WindowData &data = *(WindowData *)glfwGetWindowUserPointer(window);

        MouseMovedEvent event((float)x, (float)y);
        data.EventCallBack(event);
    });
}
void WindowsWindow::Shutdown()
{
    HZ_PROFILE_FUNC();
    glfwDestroyWindow(m_Window);
}
} // namespace Hazel